﻿// Copyright (c) egmkang wang. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Gateway
{
    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Orleans;
    using Orleans.Concurrency;
    using DotNetty.Transport.Channels;
    using XServer.GrainInterfaces;
    using XServer.SDK.Net;
    using XServer.SDK;
    using XServer.Message;

    public class GatewayHandler : MessageHandler<CSMsgDecoder, CSMessage>
    {
        
        IPlayerGrain playerGrain = null;
        static GatewayObserver gatewayObserver = new GatewayObserver();
        static IGatewayObserver ob = null;
        static string gatewayKey = String.Format("{0}:{1}", Platform.GetLocalIP(), GatewaySettings.Port);
        static OnceFlag once = new OnceFlag();

        #region 生成PlayerID
        public static readonly int PLAYER_ID_GEN_COUNT = 16;
        static IPlayerIDGrain[] PlayerIDGenerator = new IPlayerIDGrain[PLAYER_ID_GEN_COUNT];
        internal static Task<long> GetPlayerID(string account, long serverID)
        {
            var mask = (uint)(account.GetHashCode()) % PLAYER_ID_GEN_COUNT;
            if (PlayerIDGenerator[mask] == null)
            {
                PlayerIDGenerator[mask] = GrainClient.GrainFactory.GetGrain<IPlayerIDGrain>(mask);
            }
            return Task.FromResult(PlayerIDGenerator[mask].GetPlayerIDByOpenID(account, serverID).Result.Key);
        }
        #endregion

        async void ProcessLogin(IChannel channel, MsgReqLogin msg)
        {
            var openID = msg.OpenID;
            var serverID = msg.ServerID;

            var playerID = await GetPlayerID(openID, serverID);
            channel.GetAttribute(Server.OPEN_ID).Set(openID);
            channel.GetAttribute(Server.PLAYER_ID).Get().Value = playerID;
            channel.GetAttribute(Server.SERVER_ID).Get().Value = serverID;
            this.playerGrain = GrainClient.GrainFactory.GetGrain<IPlayerGrain>(playerID);
            once.CallOnce(() =>
            {
                ob = GrainClient.GrainFactory.CreateObjectReference<IGatewayObserver>(gatewayObserver).Result;
            });
            if (ob != null)
            {
                await this.playerGrain.RegisterObserver(gatewayKey, ob);
            }
            else
            {
                LoggerProvider.Logger.Error("PlayerLogin:{0}, GatewayObserver is null", openID);
            }
        }

        protected override void ProcessMessage(IChannelHandlerContext context, List<CSMessage> messages)
        {
            var channel = context.Channel;
            foreach (var msg in messages)
            {
                if (playerGrain != null)
                {
                    var task = playerGrain.RecvMessage(msg.msgid, new Immutable<byte[]>(msg.buffer));
                    task.ContinueWith(t =>
                    {
                        if (!String.IsNullOrEmpty(t.Exception.ToString()))
                        {
                            playerGrain.RecvMessage(msg.msgid, new Immutable<byte[]>(msg.buffer));
                        }
                    });
                }
                else if (msg.msgid == (ushort)MsgID.MSG_REQ_LOGIN)
                {
                    var login = Codec.NewMessage(msg.msgid, msg.buffer) as MsgReqLogin;
                    this.ProcessLogin(channel, login);
                    var task = playerGrain.RecvMessage(msg.msgid, new Immutable<byte[]>(msg.buffer));
                    task.ContinueWith(t =>
                    {
                        if (!String.IsNullOrEmpty(t.Exception.ToString()))
                        {
                            playerGrain.RecvMessage(msg.msgid, new Immutable<byte[]>(msg.buffer));
                        }
                    });
                }
                else
                {
                    LoggerProvider.Logger.Warn("SessionID:{0} MSG:{1} dropped", channel.GetSessionID(), msg.msgid);
                }
            }
        }

        protected override void OnConnectionClosing()
        {
            if (ob != null)
            {
                this.playerGrain.UnRegisterObserver(gatewayKey);
            }
        }
    }
}
